iT邦幫忙

2024 iThome 鐵人賽

DAY 21
0
自我挑戰組

自學vue~點亮Roadmap過程系列 第 21

vue3鍊成術第二十一天-列表渲染(2)

  • 分享至 

  • xImage
  •  

列表渲染(2)

通過 key 管理狀態

Vue 默認按照“就地更新”的策略來更新通過 v-for 渲染的元素列表。當數據項的順序改變時,Vue 不會隨之移動 DOM 元素的順序,而是就地更新每個元素,確保它們在原本指定的索引位置上渲染。

默認模式是高效的,但只適用於列表渲染輸出的結果不依賴子組件狀態或者臨時 DOM 狀態 (例如表單輸入值) 的情況。

為了給 Vue 一個提示,以便它可以跟蹤每個節點的標識,從而重用和重新排序現有的元素,你需要為每個元素對應的塊提供一個唯一的 key attribute:

<div v-for="item in items" :key="item.id">
  <!-- 內容 -->
</div>

當你使用 template v-for 時,key 應該被放置在這個 template 容器上:

<template v-for="todo in todos" :key="todo.name">
  <li>{{ todo.name }}</li>
</template>

注意
key 在這裡是一個通過 v-bind 綁定的特殊 attribute。請不要和在 v-for 中使用對象裡所提到的對象屬性名相混淆。
推薦在任何可行的時候為 v-for 提供一個 key attribute,除非所迭代的 DOM 內容非常簡單 (例如:不包含組件或有狀態的 DOM 元素),或者你想有意採用默認行為來提高性能。

key 綁定的值期望是一個基礎類型的值,例如字符串或 number 類型。不要用對象作為 v-for 的 key。

組件上使用 v-for

我們可以直接在組件上使用 v-for,和在一般的元素上使用沒有區別 (別忘記提供一個 key):

<MyComponent v-for="item in items" :key="item.id" />

但是,這不會自動將任何數據傳遞給組件,因為組件有自己獨立的作用域。為了將迭代後的數據傳遞到組件中,我們還需要傳遞 props:

<MyComponent
  v-for="(item, index) in items"
  :item="item"
  :index="index"
  :key="item.id"
/>

不自動將 item 注入組件的原因是,這會使組件與 v-for 的工作方式緊密耦合。明確其數據的來源可以使組件在其他情況下重用。

數組變化偵測

變更方法

Vue 能夠偵聽響應式數組的變更方法,並在它們被調用時觸發相關的更新。這些變更方法包括:

  • push()
  • pop()
  • shift()
  • unshift()
  • splice()
  • sort()
  • reverse()

替換一個數組

變更方法,顧名思義,就是會對調用它們的原數組進行變更。相對地,也有一些不可變 (immutable) 方法,例如 filter(),concat() 和 slice(),這些都不會更改原數組,而總是返回一個新數組。當遇到的是非變更方法時,我們需要將舊的數組替換為新的:

// `items` 是一個數組的 ref
items.value = items.value.filter((item) => item.message.match(/Foo/))

你可能認為這將導致 Vue 丟棄現有的 DOM 並重新渲染整個列表——幸運的是,情況並非如此。Vue 實現了一些巧妙的方法來最大化對 DOM 元素的重用,因此用另一個包含部分重疊對象的數組來做替換,仍會是一種非常高效的操作。

展示過濾或排序後的結果

有時,我們希望顯示數組經過過濾或排序後的內容,而不實際變更或重置原始數據。在這種情況下,你可以創建返回已過濾或已排序數組的計算屬性。

const numbers = ref([1, 2, 3, 4, 5])

const evenNumbers = computed(() => {
  return numbers.value.filter((n) => n % 2 === 0)
})
<li v-for="n in evenNumbers">{{ n }}</li>

在計算屬性不可行的情況下 (例如在多層嵌套的 v-for 循環中),你可以使用以下方法:

const sets = ref([
  [1, 2, 3, 4, 5],
  [6, 7, 8, 9, 10]
])

function even(numbers) {
  return numbers.filter((number) => number % 2 === 0)
}
<ul v-for="numbers in sets">
  <li v-for="n in even(numbers)">{{ n }}</li>
</ul>

在計算屬性中使用 reverse() 和 sort() 的時候務必小心!這兩個方法將變更原始數組,計算函數中不應該這麼做。請在調用這些方法之前創建一個原數組的副本:

- return numbers.reverse()
+ return [...numbers].reverse()

key

key 這個特殊的屬性主要作為 Vue 的虛擬 DOM 演算法提示,用於在比較新舊節點列表時識別 vnode。

預期:number | string | symbol

在沒有 key 的情況下,Vue 會使用一種最小化元素移動的演算法,並儘可能就地更新/複用相同類型的元素。如果傳遞了 key,則會根據 key 的變化順序重新排列元素,並且會始終移除/銷毀 key 已經不存在的元素。

同一個父元素下的子元素必須具有唯一的 key。重複的 key 會導致渲染異常。

也可以用於強制替換一個元素/組件而不是複用它。當你想這麼做時,它可能會很有用:

  • 在適當的時候觸發組件的生命周期鉤子
  • 觸發過渡效果
<transition>
  <span :key="text">{{ text }}</span>
</transition>

当 text 变化时,span 总是会被替换而不是更新,因此 transition 将会被触发。


上一篇
vue3鍊成術第二十天-列表渲染(1)
下一篇
vue3鍊成術第二十二天-事件處理(1)
系列文
自學vue~點亮Roadmap過程30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言